⚡ 30 秒速记
Vue 2属性访问器拦截已有字段- 渲染读取时,依赖记录关联当前观察者
- 写入通知相关观察者,再安排刷新
- 更新会重新收集实际使用的依赖
- 新属性与数组索引修改存在特别边界
Vue 2 响应式像给属性装上读写门铃:渲染读到它时登记依赖,值变化时通知需要更新的工作。 属性通过访问器包裹,观察者执行渲染时完成登记,写入再触发通知与调度,最后生成新界面并更新节点。关键不只是检测赋值,还要知道谁用过这个值。新增字段和某些数组操作绕不过既有访问器的限制,要使用该版本提供的更新方式。
版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。
# Vue 初始化
先从最简单的一段 Vue 代码开始:
<template>
<div>
{{ message }}
</div>
</template>
<script>
new Vue({
data() {
return {
message: "hello world",
};
},
});
</script>
- 这段代码很简单,最终会在页面上打印一个 hello world,它是如何实现的呢?
- 我们从源头:new Vue 的地方开始分析。
// 执行 new Vue 时会依次执行以下方法
// 1. Vue.prototype._init(option)
// 2. initState(vm)
// 3. observe(vm._data)
// 4. new Observer(data)
// 5. 调用 walk 方法,遍历 data 中的每一个属性,监听数据的变化。
function walk(obj) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
// 6. 执行 defineProperty 监听数据读取和设置。
function defineReactive(obj, key, val) {
// 为每个属性创建 Dep(依赖搜集的容器,后文会讲)
const dep = new Dep();
// 绑定 get、set
Object.defineProperty(obj, key, {
get() {
const value = val;
// 如果有 target 标识,则进行依赖搜集
if (Dep.target) {
dep.depend();
}
return value;
},
set(newVal) {
val = newVal;
// 修改数据时,通知页面重新渲染
dep.notify();
},
});
}